--
-- Author:      name
-- DateTime:    2018-04-27 10:59:15
-- Description: 所有对象的管理,热更

local lfs = require "lfs"
local skynet = require "skynet"
local codecache = require "skynet.codecache"

local Objects = class("Objects")

function Objects:ctor()
	self.tbObject = {}
end

function Objects:add(obj)
	--一个类可能对应很多个对象
	self.tbObject[obj.__cname] = self.tbObject[obj.__cname] or {}
	table.insert(self.tbObject[obj.__cname],obj)
end

function Objects:get(ClassName)
	--print("____ClassName ",ClassName,self.tbObject)
	return self.tbObject[ClassName]
end

function Objects:getOne(ClassName)
	if self.tbObject[ClassName] then 
		return self.tbObject[ClassName][1]
	end
	return nil
end

function Objects:getAll()
	return self.tbObject
end

--folder 服务所在文件夹
function Objects:hotfix(folder)
	--[[
		1 起动一个定时器，检查文件是否发生变动
		2.复制发生变动的文件到inject文件夹
		2.require inject下的类文件
		3.检查是否存在对象，进行类方法更新
		4 
	--]]
	package.path = "../inject/?.lua;"..package.path

	local folder = folder or ""
	local rootPath = skynet.getenv("pro_path").."/" --进程所在目录
	local servicePath = rootPath  --服务所在目录
	local injectPath = "../inject/" --复制变动文件到此文件夹	
	local fileName	
	local moduleName 
	local newModuleName
	local ext = ".lua"
	local fileList 
	local tbFileTime = {}	--文件最后一次改动的时间 		
	local tbInjectFiles = {} --生成在inject下的文件
	if not isFolderExist(injectPath) then 
		createFolder(injectPath)
	end

	if string.len(folder) > 1 then
		servicePath = rootPath .. folder .."/"
	end
	fileList = dir_list(servicePath)	
	for k,v in pairs(fileList) do 
		if get_file_ext(v) == ext and v~="Objects.lua" then
			-- print(v,lfs.attributes(servicePath..v,"change"),servicePath)
			tbFileTime[servicePath..v] = lfs.attributes(servicePath..v,"change")
		end
	end		

	--替换已加载的模块
	local function replaceModule(moduleName,newModuleName)
		print("#########replaceModule########",moduleName,newModuleName)
		if package.loaded[moduleName] then 
			local old_module = require(moduleName)
			-- package.loaded[moduleName] = nil --对skynet不起作用
			-- package.preload[moduleName] = nil
			local ok,new_module = pcall(require, newModuleName)
			if ok then
				local objs = self:get(old_module.__cname)
				-- print("##################",old_module.__cname,objs)
				if objs and next(objs) then 
				    for k,v in pairs(new_module) do			    	
				        old_module[k] = v
				        for i,o in pairs(objs) do 
					        o[k] = v
					        if o.register then 
					        	o:register()
					        end
					    end
				    end
				end
			    if objs and objs[1].test then 
				    objs[1]:test()
				end
			else
			    print("ERROR >>"..new_module)
			end
			-- if package.loaded[moduleName] ~= old_module then 
				-- codecache.clear()
			-- end
			package.loaded[moduleName] = old_module
			package.preload[moduleName] = old_module			
		end
	end

	--检查文件是否改变动
	local function loop()
		while true do 
			skynet.sleep(200)
			for k,v in pairs(tbInjectFiles) do 
				--删除文件
				print("____delete file____",v)
				-- os.remove(v)	
			end
			tbInjectFiles = {}
			
			local fileChangeTime = 0
			for k,v in pairs(tbFileTime) do 
				fileChangeTime = lfs.attributes(k,"change") or 0	
				if fileChangeTime > v then					
					fileName = get_file_name(k)
					-- print("_________fileName_change___",fileName)
					fileName = string.gsub(fileName,ext,"")
					newModuleName = fileName.. os.time()					
					tbFileTime[k] = fileChangeTime
					moduleName = fileName
					if string.len(folder) > 1 then 
						moduleName = folder ..".".. fileName	
						moduleName = string.gsub(moduleName,'../','') --database 不在一个节点中会出现'../'删除之				
					end			
					-- print("________moduleName_change___",fileName)						
					if package.loaded[moduleName] then--在模块中已存在的进行热更
						if not file_exist(injectPath..newModuleName..".lua") then 
							copyfile(servicePath..fileName..".lua",injectPath..newModuleName..".lua")
						end
						replaceModule(moduleName,newModuleName)	
						table.insert(tbInjectFiles,injectPath..newModuleName..".lua")				
					end
				end
			end
		end
	end
	skynet.fork(loop)

end

return Objects